﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Text;

namespace WebParser
{
    public class MimeReceiver : BaseService
    {
        public event EventHandler<ContentEventArgs> ContentReceived;
        public event EventHandler<ErrorEventArgs> Error;
        protected virtual void OnError(ErrorEventArgs e)
        {
            if (Error != null) Error(this, e);
        }

        public string Url { get; set; }
        public string Method { get; set; }

        private string _boundary;
        private readonly Encoding _enc = Encoding.ASCII;
        private readonly List<byte> _tmp = new List<byte>();
        private string _contentType;
        private int _contentLength;
        private Action<byte> _p;

        protected override bool PrepareStart()
        {
            return (!string.IsNullOrEmpty(Url) && !string.IsNullOrEmpty(Method));
        }
        protected override void ContinuableRunningJob()
        {
            var buffer = new byte[0x2000];
            var r = SendUrlCommand(Url, Method);
            var index = r.ContentType.IndexOf("boundary=");
            if (index < 0) return;
            _boundary = "--" + r.ContentType.Substring(index + 9);
            SwitchTo(ReadBoundary);
            using (var s = r.GetResponseStream())
            {
                if (s == null) return;
                while (KeepRunning)
                {
                    var len = s.Read(buffer, 0, buffer.Length);
                    for (var i = 0; i < len; i++)
                        _p(buffer[i]);
                }
            }
        }
        private void SwitchTo(Action<byte> function)
        {
            var source = (_p == null) ? "{EMPTY}" : _p.Method.Name;
            var target = (function == null) ? "{EMPTY}" : function.Method.Name;
            Debug.WriteLine(string.Format("{0} => {1}", source, target));
            _p = function;
        }
        private void ReadBoundary(byte b)
        {
            if (b == '\r') return;
            if (b == '\n')
            {
                var line = _enc.GetString(_tmp.ToArray());
                if (line == _boundary)
                    SwitchTo(ReadFrameHeader);
                _tmp.Clear();
                return;
            }
            _tmp.Add(b);
        }
        private void ReadFrameHeader(byte b)
        {
            if (b == '\r') return;
            if (b == '\n')
            {
                if (_tmp.Count < 1)
                {
                    SwitchTo(ReadFrameData);
                    return;
                }
                var line = _enc.GetString(_tmp.ToArray());
                var index = line.IndexOf(": ");
                if (index > -1)
                {
                    var key = line.Substring(0, index);
                    switch (key)
                    {
                        case "Content-Type":
                            _contentType = line.Substring(index + 2);
                            break;
                        case "Content-Length":
                            int len;
                            if (int.TryParse(line.Substring(index + 2), out len))
                                _contentLength = len;
                            break;
                    }
                }
                _tmp.Clear();
                return;
            }
            _tmp.Add(b);
        }
        private void ReadFrameData(byte b)
        {
            _tmp.Add(b);
            if (_tmp.Count < _contentLength) return;
            if (ContentReceived != null)
            {
                var e = new ContentEventArgs(_tmp.ToArray(), _contentType, _contentLength);
                ContentReceived(this, e);
                _contentType = null;
                _contentLength = -1;
            }
            _tmp.Clear();
            SwitchTo(ReadBoundary);
        }
        private static WebResponse SendUrlCommand(string urlCommand, string method)
        {
            var request = WebRequest.Create(urlCommand);
            request.Proxy = null;
            request.Method = method;
            return request.GetResponse();
        }
    }
}